﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ManagedNite;

namespace MediaPortal.Plugins.Sense
{
    public class ScrollFilter : XnMPointFilter
    {
        protected float pointerX;
        protected float pointerY;
        protected float pointerZ;
        public Stack<float> stabilizer;

        protected bool released = false;

        public event EventHandler<ScrollFilterEventArgs> Scroll;
        public event Action<bool> ScrollReleased;        

        public ScrollFilter()
            : base()
        {
            this.stabilizer = new Stack<float>(10);
            this.PointCreate += new EventHandler<PointBasedEventArgs>(OnFilterPointCreate);
            this.PointUpdate += new EventHandler<PointBasedEventArgs>(OnFilterPointUpdate);
        }       

        public virtual int Sensitivity
        {
            get
            {
                return this.sensitivity;
            }
            set
            {
                this.sensitivity = value;
            }
        } protected int sensitivity = 6;

        public virtual int Range
        {
            get
            {
                return this.range;
            }
            set
            {
                this.range = value;
            }
        } protected int range = 80;

        public virtual bool Inverted
        {
            get
            {
                return this.inverted;
            }
            set
            {
                this.inverted = value;
            }
        } protected bool inverted = false;

        public virtual bool ScrollOnX
        {
            get
            {
                return this.scrollOnX;
            }
            set
            {
                this.scrollOnX = value;
            }
        } protected bool scrollOnX = false;

        #region Internal

        protected virtual void OnFilterPointUpdate(object sender, PointBasedEventArgs e)
        {
            LogDebug(e);

            float x = e.Position.X;
            float y = e.Position.Y;
            float z = e.Position.Z;

            int touchDiff = pointerZ.CompareTo(z);

            if (touchDiff > 0)
            {
                if (this.stabilizer.Count == 10)
                {
                    this.stabilizer.Pop();
                }
                this.stabilizer.Push(z);
                this.pointerZ = this.stabilizer.Average();
            }
            
            int touchDelta = (int)(pointerZ - z) * touchDiff;
            if (touchDelta > this.range && touchDiff < 0)
            {
                if (!this.released)
                {
                    this.released = true;
                    raiseScrollReleased(this.released);
                }
                return;
            }
            else if (this.released)
            {
                OnFilterPointCreate(this, e);
                raiseScrollReleased(this.released);
                return;
            }

            float oldPointer = 0;
            float newPointer = 0;

            if (scrollOnX)
            {
                oldPointer = this.pointerX;
                newPointer = x;
                this.pointerY = y;
            }
            else
            {
                oldPointer = this.pointerY;
                newPointer = y;
                this.pointerX = x;
            }

            int diff = oldPointer.CompareTo(newPointer);
            
            //Logger.Debug("Diff: {0}", diff);
            if (diff == 0)
            {
                return;
            }

            int delta = (int)(oldPointer - newPointer) * diff;

            //Logger.Debug("Delta: {0}", delta);
            if (delta > this.sensitivity)
            {
                Direction action;
                int multiplier = delta / this.sensitivity;
                //Logger.Debug("Multiplier: {0}", multiplier);

                bool swap = (diff > 0);
                swap = (this.inverted) ? !swap : swap;

                if (scrollOnX)
                {
                    action = swap ? Direction.Right : Direction.Left;
                    this.pointerX = newPointer;
                }
                else
                {
                    action = swap ? Direction.Up : Direction.Down;
                    this.pointerY = newPointer;
                }

                if (Scroll != null)
                {
                    ScrollFilterEventArgs args = new ScrollFilterEventArgs(action, multiplier);
                    Scroll(this, args);
                }
            }
        }

        protected virtual void OnFilterPointCreate(object sender, PointBasedEventArgs e)
        {
            LogDebug(e);
            this.pointerX = e.Position.X;
            this.pointerY = e.Position.Y;
            this.pointerZ = e.Position.Z;
            this.released = false;
            this.stabilizer.Clear();
            this.stabilizer.Push(pointerZ);            
        }

        #endregion

        protected virtual void raiseScrollReleased(bool value)
        {
            if (ScrollReleased != null)
            {
                ScrollReleased(value);
            }
        }

        static void LogDebug(PointBasedEventArgs e) {
            //Logger.Debug("PointUpdate: ID={0}, X={1}, Y={2}, Z={3}", e.Id, e.Position.X, e.Position.Y, e.Position.Z);
        }
    }

    public class ScrollFilterEventArgs : EventArgs
    {
        public ScrollFilterEventArgs(Direction direction, int multiplier)
        {
            this.direction = direction;
            this.multiplier = multiplier;
        }

        public Direction Direction
        {
            get
            {
                return this.direction;
            }
        } Direction direction;

        public int Multiplier
        {
            get
            {
                return this.multiplier;
            }
        } int multiplier;

    }
}
